//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
//------------------------------------------------------------------------------
//compiler stuff
//------------------------------------------------------------------------------
#include <stdarg.h>
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//ulinux stuff
//------------------------------------------------------------------------------
#include <ulinux/compiler_types.h>
#include <ulinux/types.h>
#include <ulinux/sysc.h>
#include <ulinux/error.h>

#include <ulinux/socket/socket.h>
#include <ulinux/socket/un.h>
#include <ulinux/socket/msg.h>

#include <ulinux/utils/ascii/ascii.h>
#include <ulinux/utils/ascii/string/string.h>
#include <ulinux/utils/ascii/string/vsprintf.h>
#include <ulinux/utils/mem.h>
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//protocol
//------------------------------------------------------------------------------
#include <wayland/display.h>
#include <wayland/callback.h>
#include <wayland/compositor.h>
#include <wayland/registry.h>
#include <wayland/shm.h>
#include <wayland/shm_pool.h>
#include <wayland/buffer.h>
#include <wayland/seat.h>
#include <wayland/output.h>
#include <wayland/keyboard.h>
#include <wayland/pointer.h>
#include <wayland/wayland.h>
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//our private stuff
//------------------------------------------------------------------------------
#include "globals.h"
#include "wire/wl_display.h"
//------------------------------------------------------------------------------

#define REQ_GET_REGISTRY_DWS 3
void wl_display_req_get_registry(k_u32 registry_id)
{
  k_u32 req[REQ_GET_REGISTRY_DWS];
  req[0]=WL_DISPLAY_ID;
  req[1]=((REQ_GET_REGISTRY_DWS*sizeof(k_u32))<<16)|WL_DISPLAY_GET_REGISTRY;
  req[2]=registry_id;

  struct k_io_vec iov;
  iov.base=&req;
  iov.len=sizeof(req);

  struct k_msg_hdr msg;
  u_memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  k_l r;
  do r=sysc(sendmsg,3,srv_so,&msg,0);while(r==-K_EINTR);

  if(K_ISERR(r)){
    PERR("FATAL(%d):unable to send the get_registry request\n",r);
    sysc(exit_group,1,-1);
  }
  if(!r) sysc(exit_group,1,0);//server is gone
}

#define REQ_SYNC_DWS 3
void wl_display_req_sync(k_u32 callback_id)
{
  k_u32 req[REQ_SYNC_DWS];
  req[0]=WL_DISPLAY_ID;
  req[1]=((REQ_SYNC_DWS*sizeof(k_u32))<<16)|WL_DISPLAY_SYNC;
  req[2]=callback_id;

  struct k_io_vec iov;
  iov.base=&req;
  iov.len=sizeof(req);

  struct k_msg_hdr msg;
  u_memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  k_l r;
  do r=sysc(sendmsg,3,srv_so,&msg,0);while(r==-K_EINTR);

  if(K_ISERR(r)){
    PERR("FATAL(%d):unable to send the sync request\n",r);
    sysc(exit_group,1,-1);
  }
  if(!r) sysc(exit_group,1,0);//server is gone
}

static void ev_error(k_u32 *ev,void (*listener_error)(k_u32 id,k_u32 code,
                                                                     k_u8 *str))
{
  k_u16 dws=(ev[1]>>16)>>2;

  if(dws<5){
    PERRC("FATAL:display::error event, missing data\n");
    sysc(exit_group,1,-1);
  }

  if(ev[4]){
    if((k_u8*)(ev+1+1+1+1+1)+ev[4]-1>=(k_u8*)(ev+dws)){
      PERRC("FATAL:display::error event, error string bigger than the event\n");
      sysc(exit_group,1,-1);
    }
    if(*((k_u8*)(ev+1+1+1+1+1)+ev[4]-1)!=0){
      PERRC("FATAL:display::error event, error string missing null terminating"
                                                                     " char\n");
      sysc(exit_group,1,-1);
    }
  }

  static k_u8 *zero_str=(k_u8*)"no error string";
  listener_error(ev[2],ev[3],ev[4]?(k_u8*)&ev[5]:zero_str);
}

static void ev_delete_id(k_u32 *ev,void (*listener_delete_id)(k_u32 id))
{
  k_u16 dws=(ev[1]>>16)>>2;

  if(dws<3){
    PERRC("FATAL:display::delete_id event, missing object id\n");
    sysc(exit_group,1,-1);
  }

  listener_delete_id(ev[2]);
}

void wl_display_ev(k_u32 *ev,void *listener)
{
  k_u16 op=ev[1]&0xffff;

  struct wl_display_listener *display_listener=listener;
  switch(op){
  case WL_DISPLAY_ERROR:
    ev_error(ev,display_listener->error);
    break;
  case WL_DISPLAY_DELETE_ID:
    ev_delete_id(ev,display_listener->delete_id);
    break;
  }
}
